﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
using System.Threading.Tasks;

namespace Demo
{

    public class RazorViewRenderer
    {
        private readonly IRazorViewEngine _viewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;

        public RazorViewRenderer(
            IRazorViewEngine viewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _viewEngine = viewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }

        public async Task RenderViewToString(HttpContext context, string path)
        {
            var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());

            var viewEngineResult = _viewEngine.GetView(path, Path.GetFileName(path), false);

            if (!viewEngineResult.Success)
            {
                throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", Path.GetFileName(path)));
            }

            var view = viewEngineResult.View;
            using (var output = new StreamWriter(context.Response.Body))
            {
                var viewContext = new ViewContext(
                    actionContext,
                    view,
                    new ViewDataDictionary(
                        metadataProvider: new EmptyModelMetadataProvider(),
                        modelState: new ModelStateDictionary())
                    {
                        Model = null
                    },
                    new TempDataDictionary(
                        actionContext.HttpContext,
                        _tempDataProvider),
                    output,
                    new HtmlHelperOptions());

                await view.RenderAsync(viewContext);

            }
        }


        public async Task RenderViewToString<TModel>(HttpContext context, string path, TModel model)
        {

            var actionContext = new ActionContext(context, new RouteData(), new ActionDescriptor());

            var viewEngineResult = _viewEngine.GetView(path, Path.GetFileName(path), false);

            if (!viewEngineResult.Success)
            {
                throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", Path.GetFileName(path)));
            }

            var view = viewEngineResult.View;

            using (var output = new StreamWriter(context.Response.Body))
            {
                var viewContext = new ViewContext(
                    actionContext,
                    view,
                    new ViewDataDictionary<TModel>(
                        metadataProvider: new EmptyModelMetadataProvider(),
                        modelState: new ModelStateDictionary())
                    {
                        Model = model
                    },
                    new TempDataDictionary(
                        actionContext.HttpContext,
                        _tempDataProvider),
                    output,
                    new HtmlHelperOptions());

                await view.RenderAsync(viewContext);
            }
        }
    }

    public static class RazorViewRendererExtension
    {
        public static Task Render(this HttpContext context, string path)
        {
            var render = context.RequestServices.GetService<RazorViewRenderer>();
            return render.RenderViewToString(context, path);
        }

        public static Task Render<TModel>(this HttpContext context, string path, TModel model)
        {
            var render = context.RequestServices.GetService<RazorViewRenderer>();
            return render.RenderViewToString(context, path, model);
        }
    }

}
